#ifndef NTFS_H
#define NTFS_H

#include "fstypes.h"

namespace ntfs {

typedef struct
{
    UINT64 FileReferenceNumber;
} NTFS_FILE_RECORD_INPUT_BUFFER, *PNTFS_FILE_RECORD_INPUT_BUFFER;

typedef struct
{
    INT64 FileReferenceNumber;
    UINT32 FileRecordLength;
    UINT8 FileRecordBuffer[1];
} NTFS_FILE_RECORD_OUTPUT_BUFFER, *PNTFS_FILE_RECORD_OUTPUT_BUFFER;

#pragma pack(push, 1)
typedef struct
{
    UINT8 Jump[3];
    UINT8 Format[8];
    UINT16 BytesPerSector;
    UINT8 SectorsPerCluster;
    UINT16 BootSectors;
    UINT8 Mbz1;
    UINT16 Mbz2;
    UINT16 Reserved1;
    UINT8 MediaType;
    UINT16 Mbz3;
    UINT16 SectorsPerTrack;
    UINT16 NumberOfHeads;
    UINT32 PartitionOffset;
    UINT32 Reserved2[2];
    UINT64 TotalSectors;
    UINT64 MftStartLcn;
    UINT64 Mft2StartLcn;
    UINT32 ClustersPerFileRecord;
    UINT32 ClustersPerIndexBlock;
    UINT64 VolumeSerialNumber;
    UINT8 Code[0x1AE];
    UINT16 BootSignature;
} BOOT_BLOCK, *PBOOT_BLOCK;
typedef const BOOT_BLOCK *PCBOOT_BLOCK;
#pragma pack(pop)

typedef UINT8* PFILE_RECORD;

typedef INT64 LSN;

typedef struct
{
    UINT32 Type;
    UINT16 UsaOffset;
    UINT16 UsaCount;
    LSN LogSN;
} NTFS_RECORD_HEADER, *PNTFS_RECORD_HEADER; //sizeof = 16

typedef struct
{
    NTFS_RECORD_HEADER Ntfs;    //   offset: 0
    UINT16 SequenceNumber;      //   offset: 16
    UINT16 LinkCount;           //   offset: 18
    UINT16 AttributesOffset;    //   offset: 20
    UINT16 Flags;               //   offset: 22
    UINT32 BytesInUse;          //   offset: 24
    UINT32 BytesAllocated;      //   offset: 28
    UINT64 BaseFileRecord;      //   offset: 32
    UINT16 NextAttributeNumber; //   offset: 40
    UINT16 AlignmentOrReserved; //   offset: 42
    UINT32 IndexNumber;         //   offset: 44
} FILE_RECORD_HEADER, *PFILE_RECORD_HEADER; //sizeof = 48
typedef const FILE_RECORD_HEADER *PCFILE_RECORD_HEADER;


typedef enum
{
    AttributeStandardInformation = 0x10,
    AttributeAttributeList = 0x20,
    AttributeFileName = 0x30,
    AttributeObjectId = 0x40,
    AttributeSecurityDescriptor = 0x50,
    AttributeVolumeName = 0x60,
    AttributeVolumeInformation = 0x70,
    AttributeData = 0x80,
    AttributeIndexRoot = 0x90,
    AttributeIndexAllocation = 0xA0,
    AttributeBitmap = 0xB0,
    AttributeReparsePoint = 0xC0,
    AttributeEAInformation = 0xD0,
    AttributeEA = 0xE0,
    AttributePropertySet = 0xF0,
    AttributeLoggedUtilityStream = 0x100
} ATTRIBUTE_TYPE, *PATTRIBUTE_TYPE;

typedef struct
{
    ATTRIBUTE_TYPE AttributeType;
    UINT32 Length;
    BOOLEAN Nonresident;
    UINT8 NameLength;
    UINT16 NameOffset;
    UINT16 Flags;      // 0x0001 = Compressed
    UINT16 AttributeNumber;
} ATTRIBUTE, *PATTRIBUTE;
typedef const ATTRIBUTE *PCATTRIBUTE;

typedef struct
{
    ATTRIBUTE Attribute;
    UINT32 ValueLength;
    UINT16 ValueOffset;
    UINT16 Flags;     // 0x0001 = Indexed
} RESIDENT_ATTRIBUTE, *PRESIDENT_ATTRIBUTE;
typedef const RESIDENT_ATTRIBUTE *PCRESIDENT_ATTRIBUTE;

typedef struct
{
    ATTRIBUTE Attribute;
    UINT64 LowVcn;
    UINT64 HighVcn;
    UINT16 RunArrayOffset;
    UINT8 CompressionUnit;
    UINT8 AlignmentOrReserved[5];
    UINT64 AllocatedSize;
    UINT64 DataSize;
    UINT64 InitializedSize;
    UINT64 CompressedSize;    // Only when compressed
} NONRESIDENT_ATTRIBUTE, *PNONRESIDENT_ATTRIBUTE;
typedef const NONRESIDENT_ATTRIBUTE *PCNONRESIDENT_ATTRIBUTE;


typedef struct {
    ATTRIBUTE_TYPE AttributeType;
    UINT16 Length;
    UINT8 NameLength;
    UINT8 NameOffset;
    UINT64 LowVcn;
    UINT64 FileReferenceNumber;
    UINT16 AttributeNumber;
    UINT16 AlignmentOrReserved[3];
} ATTRIBUTE_LIST, *PATTRIBUTE_LIST;
typedef const ATTRIBUTE_LIST *PCATTRIBUTE_LIST;


inline
UINT64 BytesToInt64(PUINT8 pBytes, UINT8 cbSize)
{
    UINT64 Result = 0;                                                                       
    for (int i = cbSize-1; i >= 0; i--)
        Result = (Result << 8) + pBytes[i];
    return Result;
};

template <class T>
inline T* IncPTR(T* p, UINT32 inc)
{
    return (T*)(PUINT8(p) + inc);
};

inline
void USAFixupFileRecord(PFILE_RECORD fileRecord)
{
    PFILE_RECORD_HEADER file = PFILE_RECORD_HEADER(fileRecord);
    PUINT16 usa = PUINT16(IncPTR(file, file->Ntfs.UsaOffset));
    PUINT16 sector = PUINT16(file);
    for (int i = 1; i < file->Ntfs.UsaCount; i++)
    {
        sector[255] = usa[i];
        sector += 256;
    }
}

}  //  namespace ntfs

#endif // NTFS_H
